#ifndef LIST_H
#define LIST_H

#include <limits.h>		//max value of integer(list length )

#ifndef TRUE
#define TRUE 1
#endif

#ifndef FALSE
#define FALSE 0
#endif

#define LIST_SUCCESS            0
#define LIST_ERR_NOMEM          -1
#define LIST_ERR_ARGVALUE       -2
#define LIST_ERR_CALLBACK       -3
#define LIST_ERR_LEN0           -4
#define LIST_ERR_UNKNOWN        -5
#define LIST_ERR_SET_CALLBACKS  -6
#define LIST_ERR_LEN_MAX        -7
#define LIST_ERR_INCONSISTENT   -8

#ifdef __cplusplus
extern "C" {
#endif

typedef int BOOLEAN;

typedef struct node_s {
	void *data;
	struct node_s *next;	//null if at last
	struct node_s *prev;	//null if at first 
} node_t;

/* how to deal with data field during traversing the list,
 * if ( this function's return result != 0) { 
 *      if( result < 0){
 *              
 *              caller will set err field to LIST_ERR_CALLBACK 
 *      
 *      } else if ( result > 0)
 *              
 *              caller will set err field to LIST_SUCCESS
 *      }
 *      caller will return result immediately
 * }
 */
typedef int (*func_foreach_t) (void *data);

/* return data if success, else NULL
 * this function should design to allocate memory to (void *)dst, 
 *      so need a parament as type of (void **)
 */
typedef void *(*func_copy_t) (void **dst, const void *src);

/* return 0 if success
 * else the caller will set err field to LIST_ERR_CALLBACK
 */
typedef int (*func_free_t) (void *data);

/*
 * return a POSTIVE NUMBER if larger > smaller
 * return a NAGETIVE NUMBER if larger < smaller
 * else return 0
 */
typedef int (*func_cmp_t) (const void *larger, const void *smaller);

typedef struct {
	func_foreach_t foreach;
	func_copy_t copy;
	func_free_t free;
	func_cmp_t cmp;
} data_op_t;

typedef struct list_s {
	//TRUE OR FLASE, if no error occurs, the last called function will set it to FALSE
	int has_error;
	int error_code;

	/*    DO-NOT access below   */
	int __len;		//a max length limit of (INT_MAX-1)
	node_t *__first;
	node_t *__last;
	data_op_t *__default_callback;
} list_t;

/* @param:   default callback functions, NULL if not need
 *           the subsequence call to the list who need set callback operation
 *           will check this first, if NOT null, will override the default
 *           callbacks(not really write it to the struct);
 * 
 * @return:  list head if success, or NULL if error, note that even list is get 
 * success, the callback setting may fail though
 */
list_t *list_new(data_op_t * default_callback);

/* this is a deep copy of data ,so user need to assign copy funciton
 * @list_from: which list copy from
 * @func_copy: how to copy the data (refer to the behaver of list_add_first())
 * @return: new list, and new list's error field is set correctly, or NULL if
 *  something went wrong
 */
list_t *list_new_with_copy(const list_t * list_from,
			   const func_copy_t func_copy);

/* reset the callback functions
 * @return:  0 if no error
 */
int list_reset_callbacks(list_t * list, data_op_t * default_callback);

/*
 * does the list contains the data
 * @return: Postive Number indicates how many matches detected
 *          FALSE indicates that list doesn't contain that data
 *          Nagetive Number indicates an error, and error has been set
 */
int list_contains(list_t * list, const void *data, func_cmp_t func_cmp);

/*
 * compare two list
 * It returns an integer less than, equal to, or greater than zero if larger is found, 
 * respectively, to be less than, to match, or be greater than smaller.
 *
 * @ func_cmp: if this arg is NULL && default callback is null too , it will compare data in the list by direct compare
 */
long list_cmp(const list_t * larger, const list_t * smaller,
	     const func_cmp_t func_cmp);

/* return number of element in the list
 * if error, return an nagetive number
 */
int list_size(list_t * list);

/*
 * return   SUCCESS:number of elements deleted
 *          FAILURE: nagetive number ,and set error
 */
int list_del_as(list_t * list, const void *data, func_cmp_t func_cmp,
		func_free_t func_free);

/* @param:   list operating on
 * @param:   how to delete data(NULL if not need to delete(if the default
 *                              callback is NULL too))
 * @return:  0 if no error, if len==0, will still return 0 but set err to LIST_ERR_LEN0
 */
int list_del_first(list_t * list, const func_free_t func_free);

/* add data to the list (
 *       because the user may insert data structure contains
 *       more level pointers.)
 * @param:   list operating
 * @param:   data to be added
 * @param:   how to copy data field( do DIRECT ASSIGNMENT if NULL(if the default
 *                                   callback setting is NULL too) )
 * @return:  0 if no error
 */
int list_add_first(list_t * list, const void *data,
		   const func_copy_t func_copy);

/* get first nodes's data field
 * @param:   list operating on
 * @return:  data field, if len == 0, will set LIST_ERR_LEN0 and return NULL
 */
void *list_get_first(list_t * list);

/* @param:   list operating on
 * @param:   how to delete data(NULL if not need to delete(if the default
 *                               callback is NULL too))
 * @return:  0 if no error, if len==0, will still return 0 but set error to LIST_ERR_LEN0
 */
int list_del_last(list_t * list, const func_free_t func_free);

/* add data to the list ( *NOT* deep copy,
 *       because the user may insert data structure contains
 *       more level pointers.)
 * @param:   list operating
 * @param:   data to be added
 * @param:   how to copy data field( do DIRECT ASSIGNMENT if NULL(if the default
 *                                   callback setting is NULL too) )
 * @return:  0 if no error
 */
int list_add_last(list_t * list, const void *data, const func_copy_t func_copy);

/* get last nodes's data field
 * @param:   list operating on
 * @return:  data field; if len==0, will set LIST_ERR_LEN0 and return NULL
 */
void *list_get_last(list_t * list);

/* loop list (users only modify data field theirselves)
 * @param:   funciton handles the data during loop
 * @return:  see the define of func_foreach_t
 */
int list_for_each(list_t * list, const func_foreach_t func_foreach);

/*
 * clear the list
 * @param func_free:    leave it NULL if needn't to free the data field(if the
 *                          default callback is NULL too)
 * @return: 0 if no error, else a Negative number is return , and err code been set
 */
int list_clear(list_t * list, const func_free_t func_free);

/* destroy list
 * @param:   list operating on
 * @param:   how to destroy data field, NULL if need to do nothing(also according to default value)
 * @return:  NULL pointer
 */
void *list_destroy(list_t ** list, const func_free_t func_free);

/* join the second list to the rear of the front list
 * @dst:  join to
 * @src:   join from
 * if param 3 or param 4 is NULL, will use settings in dst->default_callbacks
 * @func_copy:  how to deal with the data (refer to the behaver of list_add_first)
 * @func_free:  how to destroy the data if the join failed (refer to the behaver of list_destroy)
 * @return:     joined list, and error filed is set correctly
 */
list_t *list_join(list_t * dst, const list_t * src, func_copy_t func_copy,
		  func_free_t func_free);

/* split original list into two lists, after return ,the original list will be set to NULL
 * @orig_list:  list to be splited
 * @lists:      an array[2] contains two pointers(need not allocate memory for them)
 * @num_in_first:   number of node will contains in the lists[0]
 */
list_t **list_split(list_t ** orig_list, list_t * lists[],
		    const int num_in_first);

/* swap nodes at index1 and index2 (will not move data , only change pointers)
 * will set the err_staus filed
 * @list:   list operate on
 * @index1 & @index2:   the two nodes's index
 * @return : 0 on success, else a negative number
 */
int list_swap(list_t * list, const int index1, const int index2);

/* quick sort the list
 * @list:   list to be sorted
 * @func_cmp:   compare funciton, if NULL, will use default cmp function, if
 *              NULL too,  will do a direct cmpare
 * @return:     0 on success, and error code are set properly
 */
int list_qsort(list_t *list, func_cmp_t func_cmp);

/* reverse the list
 * @list:   list to be reversed
 * @return: 0 on success, and error code are set properly
 */
int list_reverse(list_t *list);

/* get the last error
 * @return: error string can be shown on screen
 */
const char *list_get_last_error_string(list_t *list);

/* get the error string from error_code
 * @return: error string can be shown on screen
 */
const char *list_get_error_string(const int error_code);

#ifdef __cplusplus
}
#endif

#endif//list.h
